package com.piece.blog.controller;

import com.piece.admin.api.dto.SysUserDTO;
import com.piece.admin.model.TbSysUser;
import com.piece.admin.service.TbSysUserService;
import com.piece.blog.api.constant.BlogConstants;
import com.piece.blog.api.enums.BlogType;
import com.piece.blog.model.Article;
import com.piece.blog.model.ArticleInfo;
import com.piece.blog.model.BlogGrepDefine;
import com.piece.blog.service.ArticleInfoService;
import com.piece.blog.service.ArticleService;
import com.piece.blog.service.BlogGrepDefineService;
import com.piece.core.elasticsearch.support.EsSearchUtil;
import com.piece.core.framework.annotation.permission.AclPermission;
import com.piece.core.framework.annotation.permission.SubmitPermission;
import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.enums.OptionType;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.enums.sql.SqlKey;
import com.piece.core.framework.exception.PermissionException;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.Order;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.bootstrap.TreeData;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.dto.UserDetailDTO;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.file.FileUtil;
import com.piece.core.framework.util.file.ImageUtil;
import com.piece.core.framework.util.net.HtmlUtil;
import com.piece.core.framework.util.net.HttpUtil;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.security.coder.AESCoder;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.web.controller.FrameWorkController;
import com.piece.file.client.FileClientFactory;
import com.piece.file.model.TbSysFile;
import com.piece.file.service.TbSysFileService;
import com.piece.module.file.util.convert.FileConvert;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@RefreshScope
@RestController
@Api(tags = {"博客后台管理"})
@RequestMapping("/api/blog/manage")
public class BlogManageController extends FrameWorkController {

    @Value("${server.servlet.context-path:#{null}}")
    private String contextPath;

    @Resource
    private ArticleService articleService;

    @Resource
    private ArticleInfoService articleInfoService;

    @Resource
    private BlogGrepDefineService articleGrepDefineService;

    @Resource
    private TbSysUserService tbSysUserService;

    @Resource
    private TbSysFileService tbSysFileService;

    @Autowired
    private FileClientFactory fileClientFactory;

    @PostMapping("/list")
    @AclPermission(code = "blog:article:view")
    @ApiOperation(value = "搜索文章列表")
    @ApiImplicitParam(value = "查询条件", dataTypeClass = QueryTable.class, type = "body")
    public List list(UserDetailDTO userDetail) {
        QueryTable queryTable = initTable();
        // 增加排序
        List<Order> orders = queryTable.getOrder();
        queryTable.setOrder(generatorOrder(orders));
        DataTable dataTable = articleService.findPage(queryTable);

        SysUserDTO user = (SysUserDTO) userDetail.getDetail();
        List<Article> datas = dataTable.getData();
        datas = datas.stream().map(article -> {
            setEntityInfo(article, user);
            return article;
        }).collect(Collectors.toList());
        return datas;
    }

    @PostMapping("/table")
    @AclPermission(code = "blog:article:view")
    @ApiOperation(value = "查询文章分页数据")
    @ApiImplicitParam(value = "查询条件", dataTypeClass = QueryTable.class, type = "body")
    public AjaxResponse table(UserDetailDTO userDetail) {
        QueryTable queryTable = initTable();
        Map<String, Object> params = queryTable.getParams();
        // 只能查询公开或是自己的文章
        if (!userDetail.isPermitted("blog:blog")) {
            params.put("Q_open_S_EQ_OR(group1)", StatusType.VISIBLE.getCode());
            params.put("Q_createBy_L_EQ_OR(group1)", userDetail.getId());
        }
        queryTable.setParams(params);
        // 增加排序
        List<Order> orders = queryTable.getOrder();
        queryTable.setOrder(generatorOrder(orders));
        DataTable dataTable = articleService.findPage(queryTable);

        SysUserDTO user = (SysUserDTO) userDetail.getDetail();
        List<Article> datas = dataTable.getData();
        datas = datas.stream().map(article -> {
            setEntityInfo(article, user);
            return article;
        }).collect(Collectors.toList());
        dataTable.setData(datas);
        return success(dataTable);
    }

    @GetMapping("/load")
    @ApiOperation(value = "加载文章详情")
    @ApiImplicitParam(name = "id", value = "文章主键")
    public AjaxResponse load(UserDetailDTO userDetail) {
        long id = Convert.toLong(getRequest().getParameter("id"), 0L);
        Article article = new Article();
        if (id != 0) {
            try {
                article = articleService.findById(id);
                if (!hasPermission(userDetail, article, null)) {
                    throw new PermissionException(I18nUtil.message("blog.permission.error"));
                }
                ArticleInfo info = articleInfoService.findByArticle(id);
                if (null != info) {
                    article.setContent(info.getContent());
                    article.setHtml(info.getHtml());
                }
                if (StatusType.NORMAL.getCode().equals(article.getCrypto())) {
                    decryptArticle(article);
                }
                if (null == article.getEditor()) {
                    article.setContent("tinymce");
                }

                SysUserDTO user = (SysUserDTO) userDetail.getDetail();
                setEntityInfo(article, user);

                TbSysUser user_ = tbSysUserService.findById(Convert.toLong(article.getCreateBy(), 0L));
                SysUserDTO author = new SysUserDTO();
                BeanUtil.copyProperties(user_, author, true);
                article.setUser(author);
            } catch (Exception e) {
                return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), article);
            }
        } else {
            int weight = articleService.findNextIndex(null);
            article.setWeight(weight);
        }
        return success(article);
    }

    private void decryptArticle(Article article) throws Exception {
        String summary = article.getSummary();
        String content = article.getContent();
        summary = AESCoder.aesDecrypt(summary);
        content = AESCoder.aesDecrypt(content);
        article.setSummary(summary);
        article.setContent(content);
    }

    @SubmitPermission
    @PostMapping("/save")
    @ApiOperation(value = "保存文章")
    @ApiImplicitParam(value = "文章表单", dataTypeClass = Article.class, type = "body")
    public AjaxResponse save(UserDetailDTO userDetail) {
        Article article;
        try {
            Long id = Convert.toLong(getRequest().getParameter("articleId"));
            if (null == id) {
                article = new Article();
                article.setFavors(0);
                article.setViews(0);
                article.setComments(0);
                article.setFeatured("1");
                article.setCreateTime(new Date());
                article.setCreateBy(userDetail.getId());
            } else {
                article = articleService.findById(id);
            }
            initDB(article);
            if (null == article.getEditor()) {
                article.setContent("tinymce");
            }
            articleService.post(article);
            return success(I18nUtil.message(ExceptionConstants.SAVE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.SAVE_FAILURE), e.getMessage());
        }
    }

    @SubmitPermission
    @PostMapping("/saveGroupFormWithFile")
    @AclPermission(code = "blog:article:publish")
    @ApiOperation(value = "批量发布路径下文章附件")
    @ApiImplicitParams({
            @ApiImplicitParam(name="path", value = "附件路径"),
            @ApiImplicitParam(name="group", value = "文章所属分组")
    })
    public AjaxResponse saveGroupFormWithFile(UserDetailDTO userDetail) {
        try {
            String path = getRequest().getParameter("path");
            String group = getRequest().getParameter("group");
            if (StringUtil.isNotEmpty(path)) {
                // 读取文件夹
                DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(path + File.separator));
                File file;
                Article article;
                BasicFileAttributes attrs;
                String originalFilename, content, absolutePath;
                int index = 1;
                for (Path pathObj : directoryStream) {
                    // 获取文件基本属性
                    attrs = Files.readAttributes(pathObj, BasicFileAttributes.class);
                    if (!attrs.isDirectory()) {
                        absolutePath = pathObj.toAbsolutePath().toString();
                        file = new File(absolutePath);
                        originalFilename = file.getName();
                        absolutePath = URLEncoder.encode(absolutePath, "UTF-8");
                        article = new Article();
                        article.setFavors(0);
                        article.setViews(0);
                        article.setComments(0);
                        article.setWeight(100);
                        article.setFeatured(StatusType.DELETE.getCode());

                        if (FileUtil.checkAudioType(originalFilename)) {
                            article.setChannel(BlogType.VIDEO.ordinal() + "");
                            content = "<p><audio controls=\"controls\" width=\"300\" height=\"150\"><source src=\"/piece-admin/public/file/preview?path=" + absolutePath + "\"/></audio></p>";
                        } else if (FileUtil.checkVideoType(originalFilename)) {
                            article.setChannel(BlogType.VIDEO.ordinal() + "");
                            content = "<p><video controls=\"controls\" width=\"300\" height=\"150\"><source src=\"/piece-admin/public/file/preview?path=" + absolutePath + "\"/></video></p>";
                        } else {
                            continue;
                        }
                        article.setTitle(originalFilename);
                        article.setGroup(group);
                        article.setGroupIndex(index++);
                        article.setContent(content);
                        article.setSource("manual");
                        article.setStatus(StatusType.NORMAL.getCode());
                        article.setOpen(StatusType.NORMAL.getCode());
                        article.setCrypto(StatusType.DELETE.getCode());
                        article.setMemberGrowth(0L);
                        article.setCreateTime(new Date());
                        article.setCreateBy(userDetail.getId());
                        articleService.post(article);
                    }
                }
                directoryStream.close();
            }
            return success(I18nUtil.message(ExceptionConstants.SAVE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.SAVE_FAILURE), e.getMessage());
        }
    }

    @GetMapping("/loadContent")
    @ApiOperation(value = "加载文章内容")
    @ApiImplicitParam(name = "id", value = "文章主键")
    public AjaxResponse loadContent() {
        long id = Convert.toLong(getRequest().getParameter("id"), 0L);
        String content = "";

        if (id != 0) {
            Article article = articleService.findById(id);
            ArticleInfo info = articleInfoService.findByArticle(id);
            if (null != article && null != info) {
                content = info.getContent();
                if ("4".equals(article.getChannel())) {
                    List<String> src_list = Collections.singletonList(content);
                    content = src_list.get(0);
                }
            }
        }
        return success(content);
    }

    private void setEntityInfo(Article article, SysUserDTO user) {
        // 是否已收藏
        boolean hasFavored = articleService.checkFavored(user, article.getArticleId());
        article.setHasFavored(hasFavored);
        // 是否已关注
        boolean hasWatched = articleService.checkWatched(user, Convert.toLong(article.getCreateBy()));
        article.setHasWatched(hasWatched);
    }

    @SubmitPermission
    @PostMapping("/publish")
    @AclPermission(code = "blog:article:publish")
    @ApiOperation(value = "发布文章")
    @ApiImplicitParam(name = "ids", value = "文章主键集合")
    public AjaxResponse publish() {
        String ids = getRequest().getParameter("ids");
        if (StringUtil.isNotEmpty(ids)) {
            String[] id_arr = ids.split(FrameWorkConstants.SPLIT_PARAM);
            for (String id : id_arr) {
                Article article = articleService.findById(Convert.toLong(id));
                article.setStatus(StatusType.NORMAL.getCode());
                article.setCreateTime(new Date());
                articleService.post(article);
            }
            return success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS));
        }
        return success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS));
    }

    @SubmitPermission
    @PostMapping("/delete")
    @AclPermission(code = "blog:article:delete")
    @ApiOperation(value = "删除文章")
    @ApiImplicitParam(name = "ids", value = "文章主键集合")
    public AjaxResponse delete(UserDetailDTO userDetail) {
        String ids = getRequest().getParameter("ids");
        List<String> errors = new ArrayList();
        if (StringUtil.isNotEmpty(ids)) {
            Long[] idsArr = Convert.toLongArray(ids);
            for (int i = 0; i < idsArr.length; i++) {
                Article article = articleService.findById(idsArr[i]);
                try {
                    if (null != article && hasPermission(userDetail, article, null)) {
                        articleService.delete(article);
                    } else {
                        errors.add("【" + article.getTitle() + "】" + I18nUtil.message("blog.permission.error"));
                    }
                } catch (Exception e) {
                    log.error(e.getMessage());
                    errors.add("【" + article.getTitle() + "】" + e.getMessage());
                }
            }
        }

        if (errors.size() > 0) {
            return error(I18nUtil.message(ExceptionConstants.DELETE_FAILURE), CollectionUtil.convertString(errors, FrameWorkConstants.SEPARATOR_SEMICOLON));
        }
        return success(I18nUtil.message(ExceptionConstants.DELETE_SUCCESS));
    }

    @SubmitPermission
    @PostMapping("/uploadThumbnail")
    @ApiOperation(value = "上传缩略图")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "avatar_file", value = "缩略图"),
            @ApiImplicitParam(name = "avatar_data", value = "参数")
    })
    public AjaxResponse uploadThumbnail(@RequestParam("avatar_file") MultipartFile file) {
        String data = getRequest().getParameter("avatar_data");
        if (null != file) {
            try {
                InputStream is = file.getInputStream();
                byte[] bytes = ImageUtil.scale(is, 1024);
                TbSysFile sysFile = fileClientFactory.get().writeToStore(bytes, "thumbnail", file.getOriginalFilename(), file.getSize());
                return success(sysFile.getFileId());
            } catch (Exception e) {
                return error(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR), e.getMessage());
            }
        }
        return error(I18nUtil.message(ExceptionConstants.NO_SELECT));
    }

    @SubmitPermission
    @PostMapping("/uploadThumbnailFinal")
    @ApiOperation(value = "上传缩略图")
    @ApiImplicitParam(name = "avatar_file", value = "缩略图")
    public AjaxResponse uploadThumbnailFinal(@RequestParam("avatar_file") MultipartFile file) {
        if (file != null) {
            try {
                InputStream is = file.getInputStream();
                byte[] bytes = ImageUtil.scale(is, 1024);
                TbSysFile sysFile = fileClientFactory.get().writeToStore(bytes, "thumbnail", file.getOriginalFilename(), file.getSize());
                return success(sysFile.getFileId());
            } catch (Exception e) {
                return error(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR), e.getMessage());
            }
        }
        return error(I18nUtil.message(ExceptionConstants.NO_SELECT));
    }

    @SubmitPermission
    @GetMapping("/nextIndex")
    @ApiOperation(value = "查询下一个序号")
    public AjaxResponse findNextIndex() {
        String group = getRequest().getParameter("group");
        return success(articleService.findNextIndex(group));
    }

    @SubmitPermission
    @PostMapping("/featured")
    @AclPermission(code = "blog:article:manage")
    @ApiOperation(value = "推荐文章")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "文章主键"),
            @ApiImplicitParam(name = "featured", value = "推荐状态", example="0:推荐 1:未推荐")
    })
    public AjaxResponse featured() {
        String id = getRequest().getParameter("id");
        String featured = getRequest().getParameter("featured");

        try {
            Article article = articleService.findById(Convert.toLong(id));
            if (null != article) {
                article.setFeatured(featured);
                articleService.update(article);
                return success(I18nUtil.message("blog.update.success"));
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return error(I18nUtil.message("blog.update.error"));
    }

    @SubmitPermission
    @PostMapping("/top")
    @AclPermission(code = "blog:article:manage")
    @ApiOperation(value = "置顶文章")
    @ApiImplicitParam(name = "id", value = "文章主键")
    public AjaxResponse top() {
        String id = getRequest().getParameter("id");

        try {
            Article article = articleService.findById(Convert.toLong(id));
            if (null != article) {
                article.setWeight(0);
                articleService.update(article);
                return success(I18nUtil.message("blog.update.success"));
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return error(I18nUtil.message("blog.update.error"));
    }

    @GetMapping("/crawl/define/grep")
    @AclPermission(code = "spider:define:view")
    @ApiOperation(value = "查询爬取规则定义字典")
    @ApiImplicitParam(name = "key", value = "定义规则标识")
    public AjaxResponse dictDefine() {
        String key = getRequest().getParameter("key");
        Map<String, Object> param = new HashMap<>();
        if (StringUtil.isNotEmpty(key)) {
            List<BlogGrepDefine> defineList = articleGrepDefineService.findByDicGroup(key);
            for (BlogGrepDefine define : defineList) {
                param.put(define.getDictLabel(), define.getDictValue());
            }
        }
        return success(param);
    }

    @GetMapping("/crawl/define/load")
    @AclPermission(code = "spider:define:view")
    @ApiOperation(value = "加载爬取规则定义")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "id"),
            @ApiImplicitParam(name = "pid", value = "pid")
    })
    public AjaxResponse loadDefine() {
        long id = Convert.toLong(getRequest().getParameter("id"), 0L);
        Long pid = Convert.toLong(getRequest().getParameter("pid"), null);
        BlogGrepDefine define = new BlogGrepDefine();

        if (null != pid) {
            BlogGrepDefine pDefine = articleGrepDefineService.findById(pid);
            define.setParentCode(pid);
            if (null != pDefine) {
                define.setDictGroup(pDefine.getDictGroup());
                define.setDictType(OptionType.OPTION.getCode());
                define.setIsLeaf(OptionType.LEAF.getCode());
            } else {
                define.setDictType(OptionType.GROUP.getCode());
                define.setIsLeaf(OptionType.NOT_LEAF.getCode());
            }
            long dictSort = articleGrepDefineService.findNextDictSort(define);
            define.setDictSort(dictSort);
        } else if (id != 0) {
            define = articleGrepDefineService.findById(id);
        }
        return success(define);
    }

    @SubmitPermission
    @PostMapping("/crawl/define/save")
    @AclPermission(code = "spider:define:edit")
    @ApiOperation(value = "保存爬取规则定义")
    @ApiImplicitParam(value = "规则定义表单", dataTypeClass = BlogGrepDefine.class, type = "body")
    public AjaxResponse saveDefine() {
        BlogGrepDefine define = new BlogGrepDefine();
        try {
            initDB(define);
            articleGrepDefineService.insertOrUpdate(define);
            return success(I18nUtil.message(ExceptionConstants.SAVE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.SAVE_FAILURE), e.getMessage());
        }
    }

    @SubmitPermission
    @PostMapping("/crawl/define/delete")
    @AclPermission(code = "spider:define:delete")
    @ApiOperation(value = "删除爬取规则定义")
    @ApiImplicitParam(name = "ids", value = "规则定义ID集合")
    public AjaxResponse deleteDefine() {
        try {
            String ids = getRequest().getParameter("ids");
            if (StringUtil.isNotEmpty(ids)) {
                articleGrepDefineService.delete(Convert.toLongArray(ids));
            }
            return success(I18nUtil.message(ExceptionConstants.DELETE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.DELETE_FAILURE), e.getMessage());
        }
    }

    @GetMapping("/crawl/grep/select")
    @ApiOperation(value = "抓取链接下拉菜单")
    public AjaxResponse grepSelect() {
        List<String> urls = new ArrayList<>();
        List<BlogGrepDefine> defineList = articleGrepDefineService.findByDicParent();
        for (BlogGrepDefine define : defineList) {
            urls.add(define.getDictGroup());
        }

        return success(urls);
    }

    @PostMapping("/crawl/grep/treeData")
    @ApiOperation(value = "加载规则列表树")
    public List<TreeData> grepTreeData() {
        // 请求来源
        String from = getRequest().getParameter("from");
        List<TreeData> treeDataList = articleGrepDefineService.findTreeData();
        if ("vue".equals(from)) {
            TreeData root = TreeData.builder().id("0").name("规则目录").build();
            treeDataList = findChildTreeData(treeDataList, Convert.toStr(root.getId(), "0"));
            root.setChildren(treeDataList);
            List<TreeData> convertResult = new ArrayList<>();
            convertResult.add(root);
            return convertResult;
        }
        return treeDataList;
    }

    @SubmitPermission
    @PostMapping("/crawl/crawler")
    @AclPermission(code = "spider:crawl:grep")
    @ApiOperation(value = "爬取内容")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "爬取规则", dataTypeClass = Map.class, type = "body"),
            @ApiImplicitParam(name = "preview", value = "展示标识", required = false)
    })
    public AjaxResponse crawler(UserDetailDTO userDetail) {
        try {
            // 展示还是抓取
            String preview = getRequest().getParameter("preview");
            Map<String, Object> grepRule = new HashMap<>();
            initMap(grepRule);
            Article article = new Article();
            initDB(article);
            article.setArticleId(null);
            article.setFavors(0);
            article.setViews(0);
            article.setComments(0);
            article.setCrypto(StatusType.DELETE.getCode());
            article.setCreateBy(userDetail.getId());
            String base_url = (String) grepRule.get("grep_url");
            String encode;
            // 找出编码格式
            base_url = base_url.replaceAll("，", ",");

            if(base_url.indexOf(",") > -1) {
                encode = base_url.split(",")[1].trim();
                base_url = base_url.split(",")[0].trim();
            } else {
                encode = "utf-8";
            }
            String url = base_url;
            String url_define = (String) grepRule.get("grep_url_define");
            if(StringUtil.isNotEmpty(url_define)) {
                url_define = url_define.replaceAll("，", ",");
            }

            if(StringUtil.isNotEmpty(preview)) {
                article = generateByPattern(grepRule, article, base_url, encode, preview);
            } else {
                // 更新规则表
                articleGrepDefineService.updateGrepRule(base_url, grepRule);
                // 循环链接下载
                if(StringUtil.isNotEmpty(url_define)) {
                    int start = Integer.parseInt(url_define.split(FrameWorkConstants.SPLIT_PARAM)[1]);
                    int end = Integer.parseInt(url_define.split(FrameWorkConstants.SPLIT_PARAM)[2]);
                    for (int i = start; i <= end; i++) {
                        article.setArticleId(null);
                        article.setSummary(null);
                        url = base_url.replace(url_define.split(FrameWorkConstants.SPLIT_PARAM)[0], i + "");
                        try {
                            article.setContent(null);
                            article = generateByPattern(grepRule, article, url, encode, null);
                            article.setCreateTime(new Date());
                            articleService.post(article);
                        } catch (Exception e) {
                            log.error("爬取并保存文章[{}]失败：{}", url, e.getMessage());
                        }
                    }
                } else {
                    article = generateByPattern(grepRule, article, base_url, encode, null);
                    if (null == article.getArticleId()) {
                        article.setCreateBy(userDetail.getId());
                        article.setCreateTime(new Date());
                    } else {
                        article.setCreateTime(new Date());
                    }
                    articleService.post(article);
                }
            }

            return success(article);
        } catch (Exception e) {
            return error(I18nUtil.message("blog.crawl.error"), e.getMessage());
        }
    }

    /**
     * 按照正则规则获取相应数据
     * 正则表达式格式：
     * 文字：<h1.*?article-title">(.*?)</h1>.*?<div.*?"article-sub">.*?<span>(.*?)</span>.*?<span>.*?</span>.*?</div>
     * 图片：<img.*src\\s*=\\s*(.*?)[^>]*?>
     */
    private Article generateByPattern(Map<String, Object> grepRule, Article article, String url, String encode, String preview) throws Exception {
        // 抓取规则定义
        String grep_image_define = (String) grepRule.get("grep_image_define");
        String grep_title_define = (String) grepRule.get("grep_title_define");
        String grep_download_define = (String) grepRule.get("grep_download_define");
        String grep_content_define = (String) grepRule.get("grep_content_define");

        List<String> image_list = new ArrayList<>();
        List<String> title_list = new ArrayList<>();
        List<String> url_list = new ArrayList<>();
        List<String> content_list = new ArrayList<>();

        String source = HttpUtil.get(url).charset(encode).execute().text();

        if(StringUtil.isNotEmpty(grep_title_define)) {
            title_list.addAll(HtmlUtil.grepTextByPattern(source, grep_title_define, 1));
        }
        if(StringUtil.isNotEmpty(grep_image_define)) {
            image_list.addAll(HtmlUtil.grepTextByPattern(source, grep_image_define, 0));
        }
        if(StringUtil.isNotEmpty(grep_download_define)) {
            url_list.addAll(HtmlUtil.grepTextByPattern(source, grep_download_define, 1));
        }
        if (null == article.getContent() ) {
            if (StringUtil.isNotEmpty(grep_content_define)) {
                content_list.addAll(HtmlUtil.grepHtmlByAttr(source, grep_content_define.split(FrameWorkConstants.SEPARATOR_KEY_VALUE)));
            } else {
                content_list.add(source);
            }
        }

        article.setTitle(title_list.size() > 0 ? title_list.get(0) : null);
        article.setContent(content_list.size() > 0 ? content_list.get(0) : article.getContent());
        article.setSource(BlogConstants.ARTICLE_SOURCE_CRAWL);

        String thumbnail;
        if(null == preview) {
            String fileId, content = article.getContent();
            if (image_list.size() > 0) {
                String thumbnail_image = image_list.get(0);
                try {
                    thumbnail = downloadThumbnail(thumbnail_image);
                    article.setThumbnail(thumbnail);
                } catch (Exception e) {
                    log.error("爬取【" + thumbnail_image + "】图片失败：{}", e.getMessage());
                }
                // 插入图片
                for (String image : image_list) {
                    try {
                        fileId = downloadThumbnail(image);
                        content = content.replace(image, "<img src=\"" + getRequest().getContextPath() + "/admin/common/download/" + fileId + "\"/>");
                    } catch (Exception e) {
                        log.error("爬取【" + url + "】图片失败：{}", e.getMessage());
                    }
                }
            }
            if (url_list.size() > 0) {
                // 插入下载链接
                for (String download : url_list) {
                    try {
                        fileId = downloadAttach(download);
                        content = content.replace(download, getRequest().getContextPath() + "/admin/common/download/" + fileId + "");
                    } catch (Exception e) {
                        log.error("爬取【" + url + "】图片失败：{}", e.getMessage());
                    }
                }
            }
            article.setContent(content);
        }

        return article;
    }

    private String downloadThumbnail(String image) throws Exception {
        String url = "";
        Matcher matcher = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)").matcher(image);
        if (matcher.find()) {
            url = matcher.group(1);
        }
        // 网络数据下载到本地
        if (url.startsWith("http") && url.indexOf(contextPath + "/") < 0) {
            String fileName = FileUtil.generateUniqFileName(getRequest()) + "." + FileUtil.getFileSuffix(url);
            byte[] bytes = ImageUtil.download(url, 600);
            MultipartFile multipartFile = FileConvert.copy(fileName, bytes);
            try {
                InputStream is = multipartFile.getInputStream();
                bytes = ImageUtil.scale(is, 1024);
                TbSysFile sysFile = fileClientFactory.get().writeToStore(bytes, "thumbnail", multipartFile.getOriginalFilename(), multipartFile.getSize());
                return sysFile.getFileId();
            } catch (Exception e) {
                return url;
            }
        }
        return url;
    }

    private String downloadAttach(String url) throws Exception {
        // 网络数据下载到本地
        if (url.startsWith("http") && url.indexOf(contextPath + "/") < 0) {
            String fileName = FileUtil.generateUniqFileName(getRequest()) + "." + FileUtil.getFileSuffix(url);
            byte[] bytes = FileUtil.toByte(HttpUtil.get(url).execute().download());
            MultipartFile multipartFile = FileConvert.copy(fileName, bytes);
            try {
                InputStream is = multipartFile.getInputStream();
                bytes = FileUtil.toByte(is);
                TbSysFile sysFile = fileClientFactory.get().writeToStore(bytes, "upload", multipartFile.getOriginalFilename(), multipartFile.getSize());
                return sysFile.getFileId();
            } catch (Exception e) {
                return url;
            }
        }
        return url;
    }

    @PostMapping("/fileTable")
    @AclPermission(code = "blog:file:view")
    @ApiOperation(value = "查看文章附件列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page_index", value = "页号"),
            @ApiImplicitParam(name = "page_size", value = "每页数量"),
            @ApiImplicitParam(name = "channel", value = "附件类别"),
            @ApiImplicitParam(name = "table", value = "数据表名称"),
            @ApiImplicitParam(name = "business", value = "业务主键"),
            @ApiImplicitParam(name = "createBy", value = "创建人主键"),
    })
    public AjaxResponse fileTable(UserDetailDTO userDetail) {
        String pageIndex = getRequest().getParameter("page_index");
        String pageSize = getRequest().getParameter("page_size");
        String channel = getRequest().getParameter("channel");
        String table = getRequest().getParameter("table");
        String business = getRequest().getParameter("business");
        String createBy = getRequest().getParameter("createBy");
        QueryTable queryTable = new QueryTable();
        queryTable.setCurrentPage(Convert.toInt(pageIndex, 1));
        queryTable.setLength(Convert.toInt(pageSize, 20));
        queryTable.setStart((queryTable.getCurrentPage() - 1) * queryTable.getLength());
        // 增加排序
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("uploadTime", SqlKey.DESC));
        queryTable.setOrder(orders);

        Map<String, Object> param = new HashMap<>();
        if (StringUtil.isNotEmpty(channel)) {
            param.put("Q_channel_S_EQ", channel);
        }
        if (StringUtil.isNotEmpty(table)) {
            if ("isNull".equals(table)) {
                param.put("Q_table_S_NULL", table);
            } else {
                param.put("Q_table_S_EQ", table);
            }
        }
        if (StringUtil.isNotEmpty(business)) {
            param.put("Q_businessId_S_EQ", business);
        }
        if (StringUtil.isEmpty(createBy) && !userDetail.isPermitted("blog:*")) {
            param.put("Q_createBy_S_EQ", userDetail.getId());
        } else {
            param.put("Q_createBy_S_EQ", createBy);
        }

        queryTable.setParams(param);
        DataTable dataTable = tbSysFileService.findPage(queryTable);
        return success(dataTable);
    }

    @PostMapping("/clearIndex")
    @AclPermission(code = "tool:site:clear")
    @ApiOperation(value = "重置站点索引")
    public AjaxResponse clearIndex() {
        // 查出系统所有需要建立索引的文章
        List<Article> articles = articleService.articleIndex();
        // 重置索引
        try {
            EsSearchUtil.getInstance().delete(Article.class);
            EsSearchUtil.getInstance().insertBatch(articles, Article.class);
        } catch (Exception e) {
            log.error("重置博客索引异常：{}", e.getMessage());
        }

        return success();
    }

    private List<Order> generatorOrder(List<Order> orders) {
        if (null == orders) {
            orders = new ArrayList<>();
        }
        orders.add(new Order("featured", SqlKey.ASC));
        orders.add(new Order("weight", SqlKey.ASC));
        orders.add(new Order("group", SqlKey.ASC));
        orders.add(new Order("groupIndex", SqlKey.ASC));
        orders.add(new Order("createTime", SqlKey.DESC));
        return orders;
    }

    private boolean hasPermission(UserDetailDTO userDetail, Article article, String permission) {
        // 管理员拥有权限
        if (userDetail.isAdmin()) {
            return true;
        }

        // 按权限判断
        if (null != permission && userDetail.isPermitted(permission)) {
            return true;
        }

        // 按创建人判断
        if (null != article) {
            return userDetail.getId().equals(article.getCreateBy());
        }
        return true;
    }
}
